package jbpmguice.instantiation;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Stage;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GuiceConfigurationLoader {
    final Injector inject;
    private final static Logger log = Logger.getLogger(GuiceConfigurationLoader.class.getName());

    /**
     * Evil Singleton
     */
    static GuiceConfigurationLoader instance = new GuiceConfigurationLoader();

    private GuiceConfigurationLoader() {
        try {
            Collection<Module> modules = new ArrayList<Module>();
            Iterable<Module> s = load(Module.class);
            for (Module module : s) {
                modules.add(module);
            }
            inject = Guice.createInjector(Stage.PRODUCTION, modules);
        } catch (Exception e) {
            String msg = "Could not load jbpmguice.instantiation modules";
            log.log(Level.SEVERE, msg, e);
            throw new RuntimeException(msg, e);
        }
    }

    public static GuiceConfigurationLoader getConfigurationLoader() {
        return instance;
    }

    public <T> T getInstance(Class<T> type) {
        return this.inject.getInstance(type);
    }

    /**
     * Helper method to load configuration in an java 6 serviceloader like way.
     *
     * @param ifc The classname to load a configuration for. if it is e.g. com.goolge.inject.Module there
     *            has to be a  com.google.inject.Module named file in the META-INF.services directory.
     * @param <S>
     * @return
     * @throws Exception
     */
    private <S> Iterable<S> load(Class<S> ifc) throws Exception {
        ClassLoader ldr = getClass().getClassLoader();
        String s = "META-INF/services/";
        String resourceName = s + ifc.getName();
        log.log(Level.INFO, "LOG00100: Searching Guice Configuration in resource " + resourceName);
        Enumeration<URL> e = ldr.getResources(resourceName);
        Collection<S> services = new ArrayList<S>();
        while (e.hasMoreElements()) {
            URL url = e.nextElement();
            InputStream is = url.openStream();
            log.log(Level.INFO, "LOG00120: Parsing Configuration");

            try {
                BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null)
                        break;
                    // escape
                    int comment = line.indexOf('#');
                    if (comment >= 0)
                        line = line.substring(0, comment);
                    String name = line.trim();
                    if (name.length() == 0)
                        continue;
                    Class<?> clz = Class.forName(name, true, ldr);
                    Class<? extends S> impl = clz.asSubclass(ifc);
                    Constructor<? extends S> ctor = impl.getConstructor();
                    S svc = ctor.newInstance();
                    services.add(svc);
                }

            } finally {
                is.close();
            }
        }
        return services;
    }
}